local L = LibStub("AceLocale-3.0"):GetLocale("Steroids")

Steroids = CreateFrame("Frame", "Steroids", UIParent)

Steroids = LibStub("AceAddon-3.0"):NewAddon(Steroids, "Steroids", "AceConsole-3.0", "AceHook-3.0")

local db
local defaults = {
	profile = {
		lock = true,
		showSec = false,--精准模式
	}
}

local function giveOptions()
	local options = {
		type = "group",
		name = L["Steroids"],
		set = function(info,v) db[info.arg] = v; Steroids:UpdateLock() end,
		get = function(info) return db[info.arg] end,
		args = {
			lock = {
				name = L["Lock"],
				arg = "lock",
				desc = L["(Un)lock the buff frame."],
				type = "toggle",
			},
			showSec = {
				name = L["Precise"],
				arg = "showSec",
				desc = L["When you enable, buff time display hh:mm:ss"],
				type = "toggle",
			},
		}
	}
	return options
end

function Steroids:OnInitialize()
	self.db = LibStub("AceDB-3.0"):New("SteroidsDB", defaults, "Default")
	db = self.db.profile

	LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable("Steroids", giveOptions)
	LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Steroids", L["Steroids"])
	
    self:RegisterChatCommand("steroids", function() InterfaceOptionsFrame_OpenToFrame(LibStub("AceConfigDialog-3.0").BlizOptions["Steroids"].frame) end)

end

function Steroids:OnEnable()
	self:ClaimBuffFrame()
	self:SecureHook("BuffFrame_UpdateDuration")
	self:SecureHook("AuraButton_Update", "blz_AuraButton_Update")
	self:SecureHook("BuffButton_UpdateAnchors")
	self:SecureHook("BuffButtons_UpdatePositions")
	-- LoD handling
	BUFF_ROW_SPACING = 25
	self:BuffButtons_UpdatePositions()
end

local function utf8trunc(text, num)
	local len = 0
	local i = 1
	local text_len = #text
	while len < num and i <= text_len do
		len = len + 1
		local b = text:byte(i)
		if b <= 127 then
			i = i + 1
		elseif b <= 223 then
			i = i + 2
		elseif b <= 239 then
			i = i + 3
		else
			i = i + 4
		end
	end
	return text:sub(1, i-1)
end

-- FIXME: metatable this
local shorts = {}
local function getShorty(name)
	if not shorts[name] then
		local p1, p2, p3, p4 = string.split(" ", (string.gsub(name,":", " :")))
		if not p2 then
			shorts[name] = utf8trunc(name, 4)
		elseif not p3 then
			shorts[name] = utf8trunc(p1, 1) .. utf8trunc(p2, 1)
		elseif not p4 then
			shorts[name] = utf8trunc(p1, 1) .. utf8trunc(p2, 1)	.. utf8trunc(p3, 1)
		else
			shorts[name] = utf8trunc(p1, 1) .. utf8trunc(p2, 1) .. utf8trunc(p3, 1) .. utf8trunc(p4, 1)
		end
	end
	return shorts[name]
end

-- update anchors is always called from buffButton_Update but only when it really has a buff/
function Steroids:BuffButton_UpdateAnchors(buttonName, index, filter)
	local name, rank, texture, count, debuffType, duration, expirationTime = UnitAura("player", index, filter)
	-- local buffIndex, untilCancelled = UnitAura("player", index, filter)
	-- local name, rank = GetPlayerBuffName(buffIndex)
	local buff = getglobal(buttonName..index)
	if not buff.steroids then
		-- create
		buff.steroids = buff:CreateFontString(nil,"OVERLAY")
		local st = buff.steroids
		st:SetFont(GameFontNormal:GetFont())
		st:SetPoint("BOTTOM", buff, "TOP")
		st:SetJustifyH("CENTER")
		st:SetVertexColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
		st:Show()
	end

	--buff.steroids:SetText(getShorty(name)) --注解到Buff名称显示
end

function Steroids:BuffButtons_UpdatePositions()
	if SHOW_BUFF_DURATIONS == 1 then
		BUFF_ROW_SPACING = 25
	else
		BUFF_ROW_SPACING = 25 --增益间行距
	end
	BuffFrame_Update()
end

function Steroids:ClaimBuffFrame()
	self:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background"})
	self:SetFrameStrata("BACKGROUND")
	self:SetBackdropColor(0,1,0)
	self:SetWidth(280)
	self:SetHeight(225)
	self:Show()
	self:SetClampedToScreen(true)
	self:SetScript("OnDragStart", function() if not InCombatLockdown() then self:StartMoving() end end)
	self:SetScript("OnDragStop", function()
		self:StopMovingOrSizing()
		self:SavePosition()
	end)

	self:UpdateLock()
	self:RestorePosition()
	
	TemporaryEnchantFrame:ClearAllPoints()
	TemporaryEnchantFrame:SetPoint("TOPRIGHT", self, "TOPRIGHT")
	TemporaryEnchantFrame.SetPoint = function() end

	BUFF_ROW_SPACING = 25
end

function Steroids:UpdateLock()
	if db.lock then
		self:RegisterForDrag()
		self:EnableMouse(false)
		self:SetMovable(false)
		self:SetBackdropColor(0,1,0,0)
	else
		self:RegisterForDrag("LeftButton")
		self:SetBackdropColor(0,1,0,1)
		self:SetMovable(true)
		self:EnableMouse(true)
	end
end

local function SecToTime(sec)
	local txt
	if sec < 60 then
		return 0, sec
	elseif sec < 3600 then
		return floor(sec/60), sec % 60
	end
	local h = floor(sec/3600)
	sec = sec - (h*3600)
	return h, floor(sec/60)
end

--hook
function Steroids:blz_AuraButton_Update(buttonName, index, filter)
	local unit = PlayerFrame.unit;
    local name, rank, texture, count, debuffType, duration, expirationTime = UnitAura(unit, index, filter);
    local buffName = buttonName..index;
    local buff = getglobal(buffName);
    local buffDuration = getglobal(buffName.."Duration");
    if ( not name ) then
        -- No buff so hide it if it exists
        if ( buff ) then
            buff:Hide();
            buffDuration:Hide();
        end
        return nil;
    else
        local helpful = (filter == "HELPFUL");

        -- If button doesn't exist make it
        if ( not buff ) then
            if ( helpful ) then
                buff = CreateFrame("Button", buffName, BuffFrame, "BuffButtonTemplate");
            else
                buff = CreateFrame("Button", buffName, BuffFrame, "DebuffButtonTemplate");
            end

            buffDuration = getglobal(buffName.."Duration");
        end
        -- Setup Buff
        buff.namePrefix = buttonName;
        buff:SetID(index);
        buff.unit = unit;
        buff.filter = filter;
        buff:SetAlpha(1.0);
        buff:Show();

        -- Set filter-specific attributes
        if ( helpful ) then
            -- Anchor Buffs
            BuffButton_UpdateAnchors(buttonName, index);
        else
            -- Anchor Debuffs
            DebuffButton_UpdateAnchors(buttonName, index);

            -- Set color of debuff border based on dispel class.
            local debuffSlot = getglobal(buffName.."Border");
            if ( debuffSlot ) then
                local color;
                if ( debuffType ) then
                    color = DebuffTypeColor[debuffType];
                else
                    color = DebuffTypeColor["none"];
                end
                debuffSlot:SetVertexColor(color.r, color.g, color.b);
            end
        end

        if ( duration > 0 and expirationTime ) then
            if ( SHOW_BUFF_DURATIONS == "1" ) then
                buffDuration:Show();
            else
                buffDuration:Hide();
            end

            if ( not buff.timeLeft ) then
                buff.timeLeft = expirationTime - GetTime();
                buff:SetScript("OnUpdate", BuffButton_OnUpdate);
            else
                buff.timeLeft = expirationTime - GetTime();
            end
        else
            buffDuration:SetText("|cff00ff00N/A|r");
			buffDuration:SetAlpha(1)
            buffDuration:Show();
            if ( buff.timeLeft ) then
                buff:SetScript("OnUpdate", nil);
            end
            buff.timeLeft = nil;
        end

        -- Set Texture
        local icon = getglobal(buffName.."Icon");
        icon:SetTexture(texture);

        -- Set the number of applications of an aura
        local buffCount = getglobal(buffName.."Count");
        if ( count > 1 ) then
            buffCount:SetText(count);
            buffCount:Show();
        else
            buffCount:Hide();
        end

        -- Refresh tooltip
        if ( GameTooltip:IsOwned(buff) ) then
            GameTooltip:SetUnitAura(PlayerFrame.unit, index, filter);
        end
    end
    return 1;
end

function Steroids:BuffFrame_UpdateDuration(buffButton, timeLeft)
	if timeLeft then
		local duration = getglobal(buffButton:GetName().."Duration")
		if ( SHOW_BUFF_DURATIONS == "1" and timeLeft) then
			if timeLeft < BUFF_WARNING_TIME then
				duration:SetTextColor(1, 0, 0)
				if db.showSec then
					duration:SetFormattedText("%02d:%02d", SecToTime(timeLeft))
				end
				duration:SetAlpha(buffButton:GetAlpha());
			else
				duration:SetTextColor(0, 1, 0);
				if db.showSec then
					duration:SetFormattedText("%02d:%02d", SecToTime(timeLeft))
				end
			end
			duration:Show();
		else
			duration:Hide();
		end
	end
end

function Steroids:SavePosition()
	if not db.pos then db.pos = {} end
	
	local pos = db.pos
	
	local point, parent, relPoint, x, y = self:GetPoint()
	local s = self:GetEffectiveScale()
	x, y = x*s, y*s
	pos.x, pos.y = x, y
	pos.point, pos.relPoint = point, relPoint
end

function Steroids:RestorePosition()
	if not db.pos then
		local point, parent, relPoint, x, y = TemporaryEnchantFrame:GetPoint()
		self:ClearAllPoints()
		self:SetPoint(point, parent, relPoint, x, y)
	else
		local pos = db.pos

		local x, y, s = pos.x, pos.y, self:GetEffectiveScale()
		local point, relPoint = pos.point, pos.relPoint
		x, y = x/s, y/s
		self:ClearAllPoints()
		self:SetPoint(point, UIParent, relPoint, x, y)
	end
end
